﻿#region Using Directives

    using System;
    using System.Collections.Generic;
    using System.Xml.Linq;

    using Csla;

    using MarketPulse.BusinessLogic.Core;
    using MarketPulse.BusinessLogic.Common;

    using MarketPulse.BusinessLogic.Common.Criteria;
    using MarketPulse.BusinessLogic.Private.StateMachine;
    using MarketPulse.BusinessLogic.Private.BusinessRules;

#endregion

namespace MarketPulse.BusinessLogic.Private
{
    /// <summary>
    /// This class represents a visit that is made by a mystery shopper or exit interviewer (for example)
    /// when conducting either a mystery shop or exit interview or other survey.  Visits are created
    /// as part of a round and have an actual "site" or "branch" associated with them.  Visits also
    /// contain reference to the survey responses obtained when the visit and the data entry process has
    /// been completed, along with payment and charging information.  Visits have a Guid which uniquely 
    /// identifies the Visit.
    /// </summary>
    [Serializable]
    [Csla.Server.ObjectFactory("MarketPulse.DataAccess.Factory.VisitFactory, MarketPulse.DataAccess.Factory")]
    public class Visit : MarketPulseBase<Visit>
    {

        #region Member Variables

            private IVisitStateContext mVisitStateContext; 

        #endregion

        #region Enumerations

            /// <summary>
            /// Enumeration used to determine whether payment needs to be included
            /// as part of an action
            /// </summary>
            public enum PaymentStatus
            {
                /// <summary>
                ///  With Pay
                /// </summary>
                WithPay,
                
                /// <summary>
                /// Without Pay
                /// </summary>
                WithoutPay,
            }

            /// <summary>
            /// Enumeration used to determine the action taken when preparing
            /// a visit for a revisit. *CURRENTLY UNUSED*
            /// </summary>
            public enum RevisitType
            {
                /// <summary>
                /// Unassign with pay and revisit
                /// </summary>
                UnassignWithPay,

                /// <summary>
                /// Unassign without pay and revisit
                /// </summary>
                UnassignWithoutPay,

                /// <summary>
                /// Reset planned date only
                /// </summary>
                ResetPlannedDate,

                /// <summary>
                /// Null Operation (do nothing)
                /// </summary>
                NullOp
            }

        #endregion

        #region Constructors

            /// <summary>
            /// default constructor is private in order to (try to) enforce construction
            /// only via the static factory methods provided.
            /// </summary>
            private Visit()
            {
                mVisitStateContext = new VisitStateContext(VisitStatusTypeList.Created, this, true);
            }

        #endregion

        #region Methods

            #region Factory Methods

                /// <summary>
                /// Method used to create a new Visit object (via the DataPortal client proxy)
                /// </summary>
                /// <returns>The newly created Visit object</returns>
                public static Visit New()
                {
                    return DataPortal.Create<Visit>();
                }

                /// <summary>
                /// Method used to retrieve an existing Visit object from the database using the provided Guid (via the DataPortal client proxy)
                /// </summary>
                /// <param name="guid">The Guid of the Visit to retrieve</param>
                /// <returns>A valid (filled) Visit object from the database</returns>
                public static Visit Get(Guid guid)
                {
                    Visit visit = DataPortal.Fetch<Visit>(new VisitGUIDCriteria(guid));
                    visit.mVisitStateContext.SetState(VisitStateFactory.Create(visit.GetProperty(Visit.VisitStatusTypeGUIDProperty).Value, true));
                    return visit;
                }

                /// <summary>
                /// Method used to update the supplied Visit object (via the DataPortal client proxy)
                /// </summary>
                /// <param name="visit">The Visit object to update</param>
                /// <returns>A Visit object that represents the Round after the update</returns>
                public static Visit Update(Visit visit)
                {
                    return DataPortal.Update<Visit>(visit);
                }


            #endregion

            #region Business Process Methods

                #region Assign Methods

                    private static MethodInfo AssignMethod = RegisterMethod(typeof(Visit), "Assign");
                    /// <summary>
                    /// Method used to assign the Visit to a specific user based on the user guid value supplied
                    /// </summary>
                    /// <param name="userGuid">The Guid of the user to assign this Visit to</param>
                    /// <param name="plannedDateTime">The date the Visit is planned to take place</param>
                    public void Assign(Guid userGuid, DateTime plannedDateTime)
                    {
                        CanExecuteMethod(AssignMethod, true);
                        mVisitStateContext.Assign(false, userGuid, plannedDateTime);

                        UserGUID = userGuid;
                        PlannedDateTime = plannedDateTime;
                        
                        AddEvent(VisitEventTypeList.Assign);
                    }

                    private static MethodInfo UnassignMethod = RegisterMethod(typeof(Visit), "Unassign");
                    /// <summary>
                    /// Method used to unassign the Visit
                    /// </summary> 
                    /// <param name="payment">Enumeration vairable used to deterine whether payment should be honoured</param>
                    public void Unassign(PaymentStatus payment)
                    {
                        mVisitStateContext.Unassign(false, payment);
                        if (payment.Equals(PaymentStatus.WithPay))
                        {
                            // TODO Stuff to do to ensure this shopper gets paid.
                        }
                        else
                        {
                            // TODO Stuff to do to remove any payments for the shopper... sucker.
                        }

                        // Null out values to reset this visit.
                        UserGUID = null;
                        ResponseGUID = null;
                        SurveyVersionID = null;
                        PlannedDateTime = null;
                        ActualStartDateTime = null;
                        ActualEndDateTime = null;

                        // If this Visit has a response, delete it.
                        // TODO:  JSR:  I think this will not do what
                        // is intended, there are no factory methods
                        // for response update/delete
                        if (Response != null)
                        {
                            Response.Delete();
                            Response.Save();
                        }
                    }

                #endregion

                private static MethodInfo ReleaseMethod = RegisterMethod(typeof(Visit), "Release");
                /// <summary>
                /// Method used to release the Visit and make it ready to be assigned
                /// </summary>
                public void Release()
                {
                    CanExecuteMethod(ReleaseMethod, true);
                    mVisitStateContext.Release(false);
                    AddEvent(VisitEventTypeList.Release);
                }

                private static MethodInfo RevisitMethod = RegisterMethod(typeof(Visit), "Revisit");
                /// <summary>
                /// Method used to 'revisit' the Visit. If the visit is in a subset of
                /// states (typically after data entry by an external user).
                /// </summary>
                public void Revisit(RevisitType revisitType)
                {
                    CanExecuteMethod(RevisitMethod, true);
                    mVisitStateContext.Revisit(false);
                    AddEvent(VisitEventTypeList.Revisit);
                    
                    // Null out values to reset this visit.
                    UserGUID = null;
                    ResponseGUID = null;
                    SurveyVersionID = null;
                    PlannedDateTime = null;
                    ActualStartDateTime = null;
                    ActualEndDateTime = null;

                    // If this Visit has a response, delete it.
                    // TODO:  JSR:  I think this will not do what
                    // is intended, there are no factory methods
                    // for response update/delete
                    if (Response != null)
                    {
                        Response.Delete();
                        Response.Save();
                    }

                    // This call is to ensure that when the visit is next persisted
                    // the insert stored procedure is called rather than the
                    // update stored procedure.  Inserting with a non-null
                    // version ID will cause an insert with the version number
                    // incremented.
                    MarkNew();
                }

                #region DataEntryMethods

                    private static MethodInfo StartDataEntryMethod = RegisterMethod(typeof(Visit), "StartDataEntry");
                    /// <summary>
                    /// Method used to save the information for this Visit captured as part of the data entry process
                    /// </summary>
                    public void StartDataEntry()
                    {
                        CanExecuteMethod(StartDataEntryMethod, true);
                        mVisitStateContext.StartDataEntry(false);
                        AddEvent(VisitEventTypeList.StartDataEntry);
                    }

                    private static MethodInfo DataEnterMethod = RegisterMethod(typeof(Visit), "DataEnter");
                    /// <summary>
                    /// Method used to save the information for this Visit captured as part of the data entry process
                    /// </summary>
                    /// <param name="responses">The XML response set to attach to the survey instance for the Visit</param>
                    public void DataEnter(XElement responses)
                    {
                        CanExecuteMethod(DataEnterMethod, true);
                        mVisitStateContext.DataEnter(false, responses);
                        SetResponses(responses);
                    }

                    private static MethodInfo CompleteDataEntryMethod = RegisterMethod(typeof(Visit), "CompleteDataEntry");
                    /// <summary>
                    /// Method used to complete the data entry process for this Visit
                    /// </summary>
                    public void CompleteDataEntry()
                    {
                        mVisitStateContext.CompleteDataEntry(false);
                        AddEvent(VisitEventTypeList.CompleteDataEntry);
                    }

                #endregion
             
                #region QA Methods

                    private static MethodInfo QAStartDataEntryMethod = RegisterMethod(typeof(Visit), "QAStartDataEntry");
                    /// <summary>
                    /// Method used to begin the QA process for this Visit
                    /// </summary>
                    public void QAStartDataEntry()
                    {
                        CanExecuteMethod(QAStartDataEntryMethod, true);
                        mVisitStateContext.QAStartDataEntry(false);
                    }

                    private static MethodInfo QADataEnterMethod = RegisterMethod(typeof(Visit), "QADataEnter");
                    /// <summary>
                    /// Method used to enter data during the QA process for this Visit
                    /// </summary>
                    public void QADataEnter(XElement responses)
                    {
                        CanExecuteMethod(QADataEnterMethod, true);
                        mVisitStateContext.QADataEnter(false);
                        SetResponses(responses);
                    }

                    private static MethodInfo QAApproveMethod = RegisterMethod(typeof(Visit), "QAApprove");
                    /// <summary>
                    /// Method used to approve the QA of the responses gathered as part of this Visit
                    /// </summary>
                    public void QAApprove()
                    {
                        CanExecuteMethod(QAApproveMethod, true);
                        mVisitStateContext.QAApprove(false);
                    }

                    private static MethodInfo QAFailMethod = RegisterMethod(typeof(Visit), "QAFail");
                    /// <summary>
                    /// Method used to fail the QA of the responses gathered as part of this Visit
                    /// </summary>
                    public void QAFail()
                    {
                        CanExecuteMethod(QAFailMethod, true);
                        mVisitStateContext.QAFail(false);
                    }

                    private static MethodInfo QAPutOnHoldMethod = RegisterMethod(typeof(Visit), "QAPutOnHold");
                    /// <summary>
                    /// Method used to put the QA process on hold whilst other aspects of the Visit are adressed (i.e. query from the branch, clarification of an answer by the shopper etc...)
                    /// </summary>
                    public void QAPutOnHold()
                    {
                        CanExecuteMethod(QAPutOnHoldMethod, true);
                        mVisitStateContext.QAPutOnHold(false);
                    }

                    private static MethodInfo QAReopenMethod = RegisterMethod(typeof(Visit), "QAReopen");
                    /// <summary>
                    /// Method used to reopen the QA process for this Visit
                    /// </summary>
                    public void QAReopen()
                    {
                        CanExecuteMethod(QAReopenMethod, true);
                        mVisitStateContext.QAReopen(false);
                    }
                    
                    private static MethodInfo MarkReadyForTransferMethod = RegisterMethod(typeof(Visit), "MarkReadyForTransfer");
                    /// <summary>
                    /// Method used to mark this Visit as being Ready to Transfer.
                    /// </summary>
                    public void MarkReadyForTransfer()
                    {
                        CanExecuteMethod(MarkReadyForTransferMethod, true);
                        mVisitStateContext.MarkReadyForTransfer(false);
                    }

                #endregion

            #endregion

            #region System Methods

                    /// <summary>
                    /// This method interrogates the internal state machine to ascertain
                    /// if the event in question causes a valid state transition.
                    /// </summary>
                    /// <param name="eventName"></param>
                    /// <returns></returns>
                    public bool CanTransition(string eventName)
                    {
                        return mVisitStateContext.CanTransition(eventName);
                    }

            #endregion

            #region Local Methods (private)

                private void SetResponses(XElement responses)
                {
                    // stamp the visit with the survey version ID because data
                    // entry has now begun
                    SurveyVersionID = Survey.VersionID;

                    if (!FieldManager.FieldExists(ResponseProperty) && !ResponseGUID.HasValue)
                    {
                        Response response = Response.New();// (this);
                        
                        response.ResponseXML = responses;
                        Response = response;                           
                    }
                    else if (ResponseGUID.HasValue)
                    {
                        Response.ResponseXML = responses;
                    }
                }

                /// <summary>
                /// Method used to add a visit event based on the Visit Event Type GUID supplied
                /// </summary>
                /// <param name="visitEventType">The GUID for the type of visit event to add</param>
                private void AddEvent(Guid visitEventType)
                {
                    VisitEvent visitEvent = VisitEvent.New();
                    visitEvent.VisitEventTypeGUID = visitEventType;
                    visitEvent.VisitGUID = this.GUID;
                    
                    Events.Add(visitEvent);
                }                              
                
            #endregion

        #endregion

        #region Properties

            #region Association GUID Properties (Foreign Keys)

                /// <summary>
                /// The ID of the associated round. 
                /// </summary>
                public static PropertyInfo<Nullable<Guid>> RoundGUIDProperty = RegisterProperty<Nullable<Guid>>(p => p.RoundGUID);
                /// <summary>
                /// The ID of the associated round. 
                /// </summary>
                public Nullable<Guid> RoundGUID
                {
                    get { return GetProperty(RoundGUIDProperty); }
                    set { SetProperty(RoundGUIDProperty, value); }
                }

                /// <summary>
                /// The User GUID of the user who will actually be carrying out the visit.
                /// </summary>
                public static PropertyInfo<Nullable<Guid>> UserGUIDProperty = RegisterProperty<Nullable<Guid>>(p => p.UserGUID);
                /// <summary>
                /// The User GUID of the user who will actually be carrying out the visit.
                /// </summary>
                public Nullable<Guid> UserGUID
                {
                    get { return GetProperty(UserGUIDProperty); }
                    private set { SetProperty(UserGUIDProperty, value); }
                }

                /// <summary>
                /// The Branch GUID of the user who will actually be carrying out the visit.
                /// </summary>
                public static PropertyInfo<Nullable<Guid>> BranchGUIDProperty = RegisterProperty<Nullable<Guid>>(p => p.BranchGUID);
                /// <summary>
                /// The Branch GUID of the user who will actually be carrying out the visit.
                /// </summary>
                public Nullable<Guid> BranchGUID
                {
                    get { return GetProperty(BranchGUIDProperty); } 
                    // TODO figure out how to update guid and corresponding association propertiess when one or the other changes
                    private set { SetProperty(BranchGUIDProperty, value); }
                }

                /// <summary>
                /// The Response GUID of the responses obtained during/after the visit.
                /// </summary>
                public static PropertyInfo<Nullable<Guid>> ResponseGUIDProperty = RegisterProperty<Nullable<Guid>>(p => p.ResponseGUID);
                /// <summary>
                /// The Response GUID of the responses obtained during/after the visit.
                /// </summary>
                public Nullable<Guid> ResponseGUID
                {
                    get { return GetProperty(ResponseGUIDProperty); }
                    set { SetProperty(ResponseGUIDProperty, value); }
                }

                /// <summary>
                /// The Survey GUID of the survey used to carry out the visit.
                /// </summary>
                public static PropertyInfo<Nullable<Guid>> SurveyGUIDProperty = RegisterProperty<Nullable<Guid>>(p => p.SurveyGUID);
                /// <summary>
                /// The Survey GUID of the survey used to carry out the visit.
                /// </summary>
                public Nullable<Guid> SurveyGUID
                {
                    get { return GetProperty(SurveyGUIDProperty); }
                    set { SetProperty(SurveyGUIDProperty, value); }
                }

                /// <summary>
                /// The Version ID of this survey.  When used in conjunction with a SurveyID, this
                /// uniquely identifies a given survey document.
                /// </summary>
                public static PropertyInfo<Nullable<long>> SurveyVersionIDProperty = RegisterProperty<Nullable<long>>(p => p.SurveyVersionID);
                /// <summary>
                /// The Version ID of this survey.  When used in conjunction with a SurveyID, this
                /// uniquely identifies a given survey document.
                /// </summary>
                public Nullable<long> SurveyVersionID
                {
                    get { return GetProperty(SurveyVersionIDProperty); }
                    private set { SetProperty(SurveyVersionIDProperty, value); }
                }

                /// <summary>
                /// The GUID of the most recent VisitState.  WARNING:  The underlying Csla 'property' will 
                /// not stay in synch with the internal state machine.  Please get the public property.
                /// </summary>
                public static PropertyInfo<Nullable<Guid>> VisitStatusTypeGUIDProperty = RegisterProperty<Nullable<Guid>>(p => p.VisitStatusTypeGUID);
                /// <summary>
                /// The GUID of the most recent VisitState.  WARNING:  The underlying Csla 'property' will 
                /// not stay in synch with the internal state machine.  Please get the public property.
                /// </summary>
                public Nullable<Guid> VisitStatusTypeGUID
                {
                    get { return mVisitStateContext.GetState().StateGuid; }
                    internal set
                    {
                        SetProperty(VisitStatusTypeGUIDProperty, value);   
                    }
                }

            #endregion           

            /// <summary>
            /// The Version ID of this visit.  When used in conjunction with a VisitGUID, this
            /// uniquely identifies a given visit revision.
            /// </summary>
            public static PropertyInfo<long> VersionIDProperty = RegisterProperty<long>(p => p.VersionID);
            /// <summary>
            /// The Version ID of this visit.  When used in conjunction with a VisitGUID, this
            /// uniquely identifies a given visit revision.
            /// </summary>
            public long VersionID
            {
                get { return GetProperty(VersionIDProperty); }
                private set { SetProperty(VersionIDProperty, value); }
            }

            /// <summary>
            /// A human readable label for this Visit
            /// </summary>
            public static PropertyInfo<string> VisitLabelProperty = RegisterProperty<string>(p => p.Label);
            /// <summary>
            /// A human readable label for this Visit
            /// </summary>
            public string Label
            {
                get { return GetProperty(VisitLabelProperty); }
                private set { SetProperty(VisitLabelProperty, value); }
            }
                     
            #region Valid Date/Time

                /// <summary>
                /// The date and time after which the visit must occur
                /// </summary>
                public static PropertyInfo<SmartDate> ValidStartDateTimeProperty = RegisterProperty<SmartDate>(p => p.ValidStartDateTime);
                /// <summary>
                /// The date and time after which the visit must occur
                /// </summary>
                public SmartDate ValidStartDateTime
                {
                    get { return GetProperty(ValidStartDateTimeProperty); }
                    set { SetProperty(ValidStartDateTimeProperty, value); }
                }

                /// <summary>
                /// The date and time before which the visit must occur
                /// </summary>
                public static PropertyInfo<SmartDate> ValidEndDateTimeProperty = RegisterProperty<SmartDate>(p => p.ValidEndDateTime);
                /// <summary>
                /// The date and time before which the visit must occur
                /// </summary>
                public SmartDate ValidEndDateTime
                {
                    get { return GetProperty(ValidEndDateTimeProperty); }
                    set { SetProperty(ValidEndDateTimeProperty, value); }
                }

	        #endregion

            #region Planned Date/Time

                /// <summary>
                /// The planned date for this visit to occur
                /// </summary>
                public static PropertyInfo<SmartDate> PlannedDateTimeProperty = RegisterProperty<SmartDate>(p => p.PlannedDateTime);
                /// <summary>
                /// The planned date for this visit to occur
                /// </summary>
                public SmartDate PlannedDateTime
                {
                    get { return GetProperty(PlannedDateTimeProperty); }
                    set { SetProperty(PlannedDateTimeProperty, value); }
                }

            #endregion
            
            #region Actual Date/Time

                /// <summary>
                /// The actual start datetime this visit occured
                /// </summary>
                public static PropertyInfo<SmartDate> ActualStartDateTimeProperty = RegisterProperty<SmartDate>(p => p.ActualStartDateTime);
                /// <summary>
                /// The actual start datetime this visit occured
                /// </summary>
                public SmartDate ActualStartDateTime
                {
                    get { return GetProperty(ActualStartDateTimeProperty); }
                    set { SetProperty(ActualStartDateTimeProperty, value); }
                }

                /// <summary>
                /// The actual datetime this visit ended
                /// </summary>
                public static PropertyInfo<SmartDate> ActualEndDateTimeProperty = RegisterProperty<SmartDate>(p => p.ActualEndDateTime);
                /// <summary>
                /// The actual datetime this visit ended
                /// </summary>
                public SmartDate ActualEndDateTime
                {
                    get { return GetProperty(ActualEndDateTimeProperty); }
                    set { SetProperty(ActualEndDateTimeProperty, value); }
                }

	        #endregion

            #region Latest Data Entry Date/Time

                /// <summary>
                /// The latest date for data entry of this visit to occur
                /// </summary>
                public static PropertyInfo<SmartDate> LatestDataEntryDateProperty = RegisterProperty<SmartDate>(p => p.LatestDataEntryDate);
                /// <summary>
                /// The latest date for data entry of this visit to occur
                /// </summary>
                public SmartDate LatestDataEntryDate
                {
                    get { return GetProperty(LatestDataEntryDateProperty); }
                }

            #endregion

            #region Lag for Data Entry

                /// <summary>
                /// The period of time after the actual visit is made within 
                /// which data entry must be completed.
                /// </summary>
                public static PropertyInfo<TimeSpan> DataEntryLagProperty = RegisterProperty<TimeSpan>(p => p.DataEntryLag);
                /// <summary>
                /// The period of time after the actual visit is made within 
                /// which data entry must be completed.
                /// </summary>
                public TimeSpan DataEntryLag
                {
                    get { return GetProperty(DataEntryLagProperty); }
                }

                /// <summary>
                /// Whether or not Data Entry is permitted at this point in time.
                /// </summary>
                public static PropertyInfo<bool> IsDataEntryPermittedProperty = RegisterProperty<bool>(p => p.IsDataEntryPermitted);
                /// <summary>
                /// Whether or not Data Entry is permitted at this point in time.
                /// </summary>
                public bool IsDataEntryPermitted
                {
                    get { return GetProperty(IsDataEntryPermittedProperty); }
                }    

            #endregion

            #region Association Properties (Related Objects)

                ///// <summary>
                ///// The associated Reporting Visit object
                ///// </summary>
                //// TODO do we need to lazy load read only object associations?
                //private static PropertyInfo<ReportingVisit> mReportingVisitProperty = RegisterProperty<ReportingVisit>(p => p.ReportingVisit);
                //public ReportingVisit ReportingVisit
                //{
                //    get
                //    {
                //        // If we have an associated GUID value but the property hasn't been loaded yet, 
                //        // load the property otherwise just get the current property
                //        if (!FieldManager.FieldExists(mReportingVisitProperty))
                //        {
                //            // Load the property and notify CSLA that the property has changed
                //            LoadProperty(mReportingVisitProperty, ReportingVisit.Get(Client.GUID.Value, GUID.Value));
                //            OnPropertyChanged(mReportingVisitProperty);
                //            return ReadProperty(mReportingVisitProperty);
                //        }
                //        else if (FieldManager.FieldExists(mReportingVisitProperty))
                //        {
                //            // Return the current property
                //            return GetProperty(mReportingVisitProperty);
                //        }
                //        else
                //        {
                //            return null;
                //        }
                //    }
                //}

                /// <summary>
                /// The associated (read only) Branch object
                /// </summary>
                private static PropertyInfo<BranchInfo> BranchInfoProperty = RegisterProperty<BranchInfo>(p => p.Branch);
                /// <summary>
                /// The associated (read only) Branch object
                /// </summary>
                public BranchInfo Branch
                {
                    // TODO what happens when the BranchGUID changes, do we have to kick off an event to reload the Branch Property?
                    //     JSR:  not exactly, you simply need to ensure that FieldManager.FieldExists returns false
                    //            then it will be lazily loaded as normal
                    get
                    {
                        // If we have an associated GUID value but the property hasn't been loaded yet, 
                        // load the property otherwise just get the current property
                        if (!FieldManager.FieldExists(BranchInfoProperty) && BranchGUID.HasValue)
                        {
                            // Load the property and notify CSLA that the property has changed
                            LoadProperty(BranchInfoProperty, BranchInfo.Get(BranchGUID.Value));
                            OnPropertyChanged(BranchInfoProperty);
                            return ReadProperty(BranchInfoProperty);
                        }
                        else if (FieldManager.FieldExists(BranchInfoProperty))
                        {
                            // Return the current property
                            return GetProperty(BranchInfoProperty);
                        }
                        else
                        {
                            return null;
                        }
                    }
                    set
                    {
                        // Set the associated GUID value and the association property (this) value
                        LoadProperty(BranchGUIDProperty, value.GUID);
                        LoadProperty(BranchInfoProperty, value);
                    }
                }

                ///// <summary>
                ///// The associated (read only) Round object
                ///// </summary>                
                //private static PropertyInfo<RoundInfo> RoundInfoProperty = RegisterProperty<RoundInfo>(p => p.Round);
                ///// <summary>
                ///// The associated (read only) Round object
                ///// </summary>   
                //public RoundInfo Round
                //{
                //    // TODO what happens when the UserGUID changes, do we have to kick off an event to reload the Round Property?
                //    get
                //    {
                //        // If we have an associated GUID value but the property hasn't been loaded yet, load the property otherwise just get the current property
                //        if (!FieldManager.FieldExists(RoundInfoProperty) && RoundGUID.HasValue)
                //        {
                //            // Load the property and notify CSLA that the property has changed
                //            LoadProperty(RoundInfoProperty, RoundInfo.Get(this.RoundGUID.Value));
                //            OnPropertyChanged(RoundInfoProperty);
                //            return ReadProperty(RoundInfoProperty);
                //        }
                //        else if (FieldManager.FieldExists(RoundInfoProperty))
                //        {
                //            // Return the current property
                //            return GetProperty(RoundInfoProperty);
                //        }
                //        else
                //        {
                //            return null;
                //        }
                //    }
                //    set
                //    {
                //        // Set the associated GUID value and the association property (this) value
                //        LoadProperty(RoundGUIDProperty, value.GUID);
                //        LoadProperty(RoundInfoProperty, value);
                //    }
                //}

                /// <summary>
                /// The associated (read only) User object
                /// </summary>                
                private static PropertyInfo<UserInfo> UserInfoProperty = RegisterProperty<UserInfo>(p => p.User);
                /// <summary>
                /// The associated (read only) User object
                /// </summary>  
                public UserInfo User
                {
                    // TODO what happens when the UserGUID changes, do we have to kick off an event to reload the User Property?
                    get
                    {
                        // If we have an associated GUID value but the property hasn't been loaded yet, load the property otherwise just get the current property
                        if (!FieldManager.FieldExists(UserInfoProperty) && UserGUID.HasValue)
                        {
                            // Load the property and notify CSLA that the property has changed
                            LoadProperty(UserInfoProperty, UserInfo.Get(UserGUID.Value));
                            OnPropertyChanged(UserInfoProperty);
                        }
                        return ReadProperty(UserInfoProperty);
                    }
                    set
                    {
                        // Set the associated GUID value and the association property (this) value
                        LoadProperty(UserGUIDProperty, value.GUID.Value);
                        LoadProperty(UserInfoProperty, value);
                    }
                }

                /// <summary>
                /// The XML document that describes the survey and its dependencies.  
                /// </summary>
                public static PropertyInfo<Survey> SurveyProperty = RegisterProperty<Survey>(p => p.Survey);
                /// <summary>
                /// The XML document that describes the survey and its dependencies.  
                /// </summary>
                public Survey Survey
                {
                    get
                    {
                        // If we have an associated GUID value but the property hasn't been loaded yet
                        if (!FieldManager.FieldExists(SurveyProperty) && SurveyGUID.HasValue)
                        {
                            // Check if there is a version ID for the survey, if not get the latest (no VersionID) otherwise get the specific Survey (GUID and VersionID)
                            if (!SurveyVersionID.HasValue)
                            {
                                return Survey.Get(SurveyGUID.Value);
                            }
                            else
                            {
                                return Survey.Get(SurveyGUID.Value, SurveyVersionID.Value);
                            }
                        }
                        else
                        {
                            return null;
                        }
                    }
                }

                /// <summary>
                /// The associated response object
                /// </summary>
                public static PropertyInfo<Response> ResponseProperty = RegisterProperty<Response>(p => p.Response);
                /// <summary>
                /// The associated response object
                /// </summary>
                public Response Response
                {
                    get
                    {
                        // If we have an associated GUID value but the property hasn't been loaded yet, load the property otherwise just get the current property
                        if (!FieldManager.FieldExists(ResponseProperty) && ResponseGUID.HasValue)
                        {
                            // Load the property and notify CSLA that the property has changed
                            LoadProperty(ResponseProperty, Response.Get(ResponseGUID.Value));
                            OnPropertyChanged(ResponseProperty);
                            return ReadProperty(ResponseProperty);
                        }
                        else if (FieldManager.FieldExists(ResponseProperty))
                        {
                            // Return the current property
                            SetProperty(ResponseGUIDProperty, GetProperty(ResponseProperty).GUID);
                            return GetProperty(ResponseProperty);                            
                        }
                        else
                        {
                            return null;
                        }
                    }
                    set
                    {
                        // Set the associated GUID value and the association property (this) value
                        SetProperty(ResponseGUIDProperty, value.GUID);
                        SetProperty(ResponseProperty, value);
                    }
                }

                /// <summary>
                /// A collection of visit events that the Visit has experienced  
                /// </summary>
                public static PropertyInfo<VisitEventList> VisitEventListProperty = RegisterProperty<VisitEventList>(p => p.Events);
                /// <summary>
                /// A collection of visit events that the Visit has experienced  
                /// </summary>
                public VisitEventList Events
                {
                    get
                    {
                        // If the property hasn't been loaded yet
                        if (!FieldManager.FieldExists(VisitEventListProperty))
                        {
                            if (IsNew)
                            {
                                // TODO check this new() method as it doesn't exist in the DataAccess!!!
                                // Load the property and notify CSLA that the property has changed
                                LoadProperty(VisitEventListProperty, VisitEventList.New());
                                OnPropertyChanged(VisitEventListProperty);
                            }
                            else
                            {
                                // Load the property
                                LoadProperty(VisitEventListProperty, VisitEventList.Get(this.GUID.Value));                                 
                            }                            
                            return ReadProperty(VisitEventListProperty);                   
                        }
                        else
                        {
                            // Return the current property
                            return GetProperty(VisitEventListProperty);
                        }
                    }
                }

                /// <summary>
                /// A collection of visit events that the Visit has experienced  
                /// </summary>
                public static PropertyInfo<CompanyInfo> mClientProperty = RegisterProperty<CompanyInfo>(p => p.Client);
                /// <summary>
                /// A collection of visit events that the Visit has experienced  
                /// </summary>
                public CompanyInfo Client
                {
                    get
                    {
                        // If the property hasn't been loaded yet
                        if (!FieldManager.FieldExists(mClientProperty))
                        {
                            // Load the property
                            LoadProperty(mClientProperty, CompanyInfo.Fetch(BranchGUID.Value));
                        }
                    
                        // Return the current property
                        return GetProperty(mClientProperty);
                    }
                }               
		 
	        #endregion                     

        #endregion

        #region Business Rules

            /// <summary>
            /// Adds business rules associated with this Business Object
            /// </summary>
            protected override void AddBusinessRules()
            {
                //BusinessRules.AddRule(new EndDateGTStartDate(mValidStartDateTimeProperty,
                //                                             mValidEndDateTimeProperty));
                //BusinessRules.AddRule(new StartDateLTEndDate(mValidEndDateTimeProperty,
                //                                             mValidStartDateTimeProperty));
                BusinessRules.AddRule(new EndDateGTStartDate(ActualStartDateTimeProperty,
                                                             ActualEndDateTimeProperty));
                //BusinessRules.AddRule(new StartDateLTEndDate(mActualEndDateTimeProperty,
                //                                             mActualStartDateTimeProperty));
                //BusinessRules.AddRule(new DerivedLatestDataEntry(mActualEndDateTimeProperty,
                //                                                    mDataEntryLagProperty,
                //                                                    mLatestDataEntryDateProperty,
                //                                                    mIsDataEntryPermittedProperty));

                //BusinessRules.AddRule(new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, "DataEnter", "Shopper"));
                //BusinessRules.AddRule(new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, "QADataEnter", "QA User"));
                //BusinessRules.AddRule(new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, "QAApprove", "QA User"));
                //BusinessRules.AddRule(new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, "QAFail", "QA User"));
                //BusinessRules.AddRule(new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, "QAPutOnHold", "QA User"));
                //BusinessRules.AddRule(new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, "QAReopen", "QA User"));
            
            }

            /// <summary>
            /// Adds business rules associated with this *type*
            /// </summary>
            protected static void AddObjectAuthorizationRules()
            {            
                //Csla.Rules.BusinessRules.AddRule(typeof(Visit), new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, StartDataEntryMethod, new List<string>{ "Shopper" }));
                //Csla.Rules.BusinessRules.AddRule(typeof(Visit), new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, DataEnterMethod, new List<string> { "Shopper" }));
                //Csla.Rules.BusinessRules.AddRule(typeof(Visit), new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, CompleteDataEntryMethod, new List<string> { "Shopper" }));
                //Csla.Rules.BusinessRules.AddRule(typeof(Visit), new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, AssignMethod, new List<string> { "Operations User", "Operations Manager" }));   
                //Csla.Rules.BusinessRules.AddRule(typeof(Visit), new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, ReleaseMethod, new List<string> { "Operations Manager" }));            
                //Csla.Rules.BusinessRules.AddRule(typeof(Visit), new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, QAStartDataEntryMethod, new List<string>{ "QA User", "Operations Manager" }));
                //Csla.Rules.BusinessRules.AddRule(typeof(Visit), new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, UnassignMethod, new List<string> { "Operations User", "Operations Manager" }));
                //Csla.Rules.BusinessRules.AddRule(typeof(Visit), new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.ExecuteMethod, RevisitMethod, new List<string> { "Operations User", "Operations Manager" }));
            }

        #endregion

    }
}
